//******************************************************************
//******************************************************************
//**********          ANts Peer To Peer Sources        *************
//
// ANts P2P realizes a third generation P2P net. It protects your
// privacy while you are connected and makes you not trackable, hiding
// your identity (ip) and crypting everything you are sending/receiving
// from others.

// Copyright (C) 2004  Roberto Rossi

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

package ants.p2p;

import java.util.*;
import ants.p2p.messages.*;
import ants.p2p.messages.security.*;
import ants.p2p.exceptions.*;

import org.apache.log4j.*;

public class Router
    extends Thread {

  static Logger _logger = Logger.getLogger(Router.class.getName());

  Ant ant = null;
  Message m;
  String requirer;
  boolean notifiedNetModification = false;

  public static double jumpProbability = 0; //NON USATO!!!
  public static int delay = 0;
  public static boolean PARALLEL = true;

  public static int initialRounds = (int)Math.floor(Math.random()*10);

  public static long routeTimeout = 120000;

  RouterWatchDog rw = null;

  public SenderThread[] st = null;

  public boolean routingMessages = false;

  public int routeProbability = 15 + (int)(System.currentTimeMillis() % 30);
  public int queryDieProbability = 15 + (int)(System.currentTimeMillis() % 30);
  public int querySeedModificationProbability = 50 + (int)(System.currentTimeMillis() % 25);

  public Router() {
    this.rw = new RouterWatchDog(this);
    this.setPriority(10);
  }

  public Router(Ant n, Message m, String requirer) {
    this.ant = n;
    this.m = m;
    this.requirer = requirer;
    this.rw = new RouterWatchDog(this);
    this.setPriority(10);
  }

  public String getRequirer(){
    return this.requirer;
  }

  public void run() {
    try {
      rw.start();
      if(((m.getDest().equals(this.ant.getIdent()) || m.getType() == 2) &&
          MessageSigner.getInstance().verifySize(m) &&
          MessageSigner.getInstance().verifySignature(m, m.getSource())) ||
         MessageSigner.getInstance().verifySize(m)){
        st = this.ant.route(null, m, this.requirer, this, true, false);
        this.routingMessages = true;
        if (st != null) {
          for (int x = 0; x < st.length; x++) {
            try {
              st[x].join(Router.routeTimeout + 60000);
            }
            catch (Exception e) {
              _logger.error("Cannot join send process", e);
            }
          }
        }
      }else{
        _logger.error("Invalid message received:\n"+m.getDest()+"\n"+this.ant.getShortId());
      }
      rw.terminate();
    }
    catch (Throwable ex) {
      rw.terminate();
      _logger.error(this.ant.getShortId() + " Router error: ",  ex);
    }
  }
}

class RouterWatchDog extends Thread{

  static Logger _logger = Logger.getLogger(RouterWatchDog.class.getName());

  Router r = null;

  boolean terminate = false;

  public RouterWatchDog(Router r){
    super();
    this.r = r;
    this.setPriority(1);
  }

  public void terminate(){
    this.terminate = true;
  }

  public void run(){
    long init = System.currentTimeMillis();
    while (!terminate) {
      try {
        sleep(5000);
      }
      catch (Exception e) {
        _logger.error("",e);
      }
      long elapsed = System.currentTimeMillis() - init;
      if(elapsed > Router.routeTimeout && !terminate){
        _logger.info("Routing stopped...");
        for(int x = 0; r.st != null && x < r.st.length; x++){
          try{
            if (r.st[x] != null) {
              r.st[x].interruptSending();
            }
          }catch(ThreadDeath td){_logger.info("Routing timed out: message killed");}
        }
        try{
          if(r != null)
            r.interrupt();
        }catch(ThreadDeath td){_logger.info("Routing timed out: message killed");}
        try {
          while(r.isAlive()){
            r.join(5000);
            _logger.warn("[" + Ant.beingRoutedMessages + "]Router waiting for " +
                         r.m + "  routing = " + r.routingMessages);
            if (r.m instanceof ants.p2p.query.QueryMessage) {
              _logger.info(((ants.p2p.query.QueryMessage)r.m).getQuery()+"   "+r.m.getType());
            }
          }
        }
        catch (InterruptedException ex) {
        }
        Ant.decBeingRoutedMessages();
        return;
      }
      _logger.debug("[" + Ant.beingRoutedMessages + "]Router idle for " +
                         r.m + "  routing = " + r.routingMessages);
    }
    Ant.decBeingRoutedMessages();
  }
}
